package de.herberlin.server.ui;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyVetoException;
import java.io.InputStream;
import java.util.Hashtable;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import java.util.prefs.BackingStoreException;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JDesktopPane;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.ListSelectionModel;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import de.herberlin.server.common.AbstractServer;
import de.herberlin.server.common.ConfigConstants;
import de.herberlin.server.common.Configuration;
import de.herberlin.server.common.TempFileHandler;
import de.herberlin.server.common.event.ApplicationEvent;
import de.herberlin.server.common.event.ApplicationEventListener;
import de.herberlin.server.common.event.EventDispatcher;
import de.herberlin.server.common.event.FileData;
import de.herberlin.server.common.event.TimeData;
import de.herberlin.server.httpd.HttpServer;
import de.herberlin.server.mail.MailHeader;
import de.herberlin.server.mail.MailServer;
import de.herberlin.server.proxy.ProxyServer;
import de.herberlin.wwwutil.RequestHeader;
import de.herberlin.wwwutil.ResponseHeader;

/**
 * Application Main Gui Class.
 *
 *
 * @author Hans Joachim Herbertz
 * @created 27.01.2003
 */
public class Main extends JFrame implements ApplicationEventListener,
		ConfigConstants {

	/**
	 * Logger
	 */
	private Logger logger = Logger.getLogger(getClass().getName());

	/**
	 * Server either HTTP or Proxy
	 */
	private AbstractServer server = null;

	/**
	 * Constructor. Sets up userinterface.
	 */
	public Main() {
		super(VERSION);

		setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);

		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				exitApplication();
			}
		});
		Container c = getContentPane();
		c.setLayout(new BorderLayout());
		JSplitPane split = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
				getDesktopPane(), getLogPanel());
		split.setOneTouchExpandable(true);
		split.setResizeWeight(1);
		c.add(split, BorderLayout.CENTER);
		c.add(getToolBar(), BorderLayout.NORTH);
		c.add(getStatusPanel(), BorderLayout.SOUTH);
		pack();
		setVisible(true);
		EventDispatcher.addApplicationListener(this);
	}

	/**
	 * Called when application shuts down. Stops server and stores configuration
	 * to filesystem.
	 *
	 */
	private void exitApplication() {
		try {
			if (server != null && server.isAlive()) {
				server.stop();
			}
			Configuration.store();
			TempFileHandler.deleteTempFiles();

		} catch (Exception io) {
			logger.throwing("exitApplication", io.getLocalizedMessage(), io);
		}
		System.exit(0);
	}

	/**
	 * Label displays connection count.
	 */
	private JLabel connectionCount = null;

	/**
	 * Creates the status panel at the bottom.
	 *
	 * @return
	 */
	private JComponent getStatusPanel() {
		JPanel p = new JPanel(new BorderLayout());
		connectionCount = new JLabel("Connections: 0");
		p.add(connectionCount, BorderLayout.WEST);

		JCheckBox logPanelFilter = new JCheckBox("Text Only");
		logPanelFilter.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				JCheckBox c = (JCheckBox) e.getSource();
				if (c.isSelected()) {
					Configuration.getPrefs().put(DISPLAY_TEXTONLY, "true");
				} else {
					Configuration.getPrefs().put(DISPLAY_TEXTONLY, "false");
				}
			}
		});
		if (Configuration.getPrefs().getBoolean(DISPLAY_TEXTONLY, false)) {
			logPanelFilter.setSelected(true);
		}
		p.add(logPanelFilter, BorderLayout.EAST);
		return p;
	}

	/**
	 * The event display table.
	 */
	private JTable table = null;

	/**
	 * Creates the log panel.
	 *
	 * @return
	 */
	private JComponent getLogPanel() {
		JPanel p = new JPanel();
		p.setLayout(new BorderLayout());
		table = new JTable(EventDispatcher.getModel());
		table.setCellSelectionEnabled(true);
		table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		table.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN);
		table.getTableHeader().setReorderingAllowed(false);
		table.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				displayDetails();
			}
		});
		JScrollPane scroll = new JScrollPane(table);
		p.add(scroll, BorderLayout.CENTER);
		p.setPreferredSize(new Dimension(120, 120));
		return p;
	}

	/**
	 * Switch between different servers, e.g http or proxy server.
	 */
	private void toggleServer(JButton button) {

		if (server != null && server.isAlive())
			server.stop();

		String oldMode=getMode();
		if (MODE_HTTP.equals(oldMode)) {
			button.setText("Proxy");
			server = new ProxyServer();
			mode = MODE_PROXY;
		} else if (MODE_PROXY.equals(oldMode)) {
			button.setText("Mail");
			server = new MailServer();
			mode = MODE_MAIL;
		} else if (MODE_MAIL.equals(oldMode)) {
			button.setText("Http");
			server = new HttpServer();
			mode = MODE_HTTP;
		} else {
			logger.warning("Unknown mode: " + getMode());
		}
//		Configuration.getPrefs().put(SERVER_SERVER,
//				button.getText().toLowerCase());
		logger.fine("toggleServer to " + server);
	}

	/**
	 * Shows the configuration dialog
	 *
	 */
	private void showConfigDialog() {
		try {
			new ConfigurationDialog(this);
		} catch (BackingStoreException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Displays details of the selected table cell on opening the respected
	 * Object display, resizing it and moving it to the front.
	 */
	private void displayDetails() {
		Object toDisplay = (table.getModel().getValueAt(table.getSelectedRow(),
				table.getSelectedColumn()));
		if (toDisplay instanceof RequestHeader) {
			requestDisplay.setObject(toDisplay);
		} else if (toDisplay instanceof MailHeader) {
			responseDisplay.setObject(toDisplay);
		} else if (toDisplay instanceof ResponseHeader) {
			responseDisplay.setObject(toDisplay);
		} else if (toDisplay instanceof TimeData) {
			timeDisplay.setObject(toDisplay);
		} else if (toDisplay instanceof FileData) {
			fileDisplay.setObject(toDisplay);
		}

	}

	/**
	 * The start stop button.
	 */
	private JToggleButton startStopButton = null;

	/**
	 * Creates the toolbar.
	 *
	 * @return
	 */
	private JToolBar getToolBar() {

		JToolBar toolBar = new JToolBar("Server");
		toolBar.setFloatable(true);

		// Proxy / Http / Mail Server switch
		JButton serverModeButton = new JButton();

		if (MODE_HTTP.equals(getMode())) {
			serverModeButton.setText("Http");
			server = new HttpServer();
		} else if (MODE_PROXY.equals(getMode())) {
			serverModeButton.setText("Proxy");
			server = new ProxyServer();
		} else {
			serverModeButton.setText("Mail");
			server = new MailServer();
		}
		serverModeButton.setToolTipText("Switch Server Mode");
		serverModeButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				toggleServer((JButton) e.getSource());
			}
		});
		toolBar.add(serverModeButton);

		// Server start stop button
		startStopButton = new JToggleButton("Start");
		startStopButton.setToolTipText("Start Server");
		startStopButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent a) {
				if (((JToggleButton) a.getSource()).isSelected()) {
					server.start();
				} else {
					server.stop();
				}
			}
		});
		toolBar.add(startStopButton);

		// Speed slider
		JSlider slider = new JSlider(0, 10);
		int initValue = 0;
		if (isHttpMode()) {
			initValue = Configuration.getPrefs().getInt(
					MODE_HTTP + SETTING_DELAY, 0);
		} else {
			initValue = Configuration.getPrefs().getInt(
					MODE_PROXY + SETTING_DELAY, 0);
		}
		if (initValue <= slider.getMaximum()
				&& initValue >= slider.getMinimum()) {
			slider.setValue(initValue);
		} else {
			slider.setValue(0);
			if (isHttpMode()) {
				Configuration.getPrefs().put(MODE_HTTP + SETTING_DELAY, 0 + "");
			} else {
				Configuration.getPrefs()
						.put(MODE_PROXY + SETTING_DELAY, 0 + "");
			}
		}
		slider.setMajorTickSpacing(2);
		slider.setMinorTickSpacing(1);
		slider.setSnapToTicks(true);
		slider.setPaintTicks(false);
		slider.setPaintLabels(true);
		Hashtable lableMap = new Hashtable(2);
		lableMap.put(new Integer(1), new JLabel("Fast"));
		lableMap.put(new Integer(9), new JLabel("Slow"));
		slider.setLabelTable(lableMap);
		slider.addChangeListener(new ChangeListener() {
			public void stateChanged(ChangeEvent e) {
				int newValue = ((JSlider) e.getSource()).getValue();
				logger.fine("newValue=" + newValue);
				Configuration.getPrefs().put(getMode() + SETTING_DELAY,
						newValue + "");
			}
		});
		toolBar.add(slider);
		toolBar.addSeparator();

		// Option buttion
		JButton configButton = new JButton();
		configButton.setText("Options");
		configButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent a) {
				showConfigDialog();
			}
		});
		toolBar.add(configButton);

		// Help button
		JButton help = new JButton("Help");
		help.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				Help.show("/");
			}
		});
		toolBar.add(help);

		return toolBar;
	}

	private InternalObjectDisplay requestDisplay = new InternalObjectDisplay();

	private InternalObjectDisplay responseDisplay = new InternalObjectDisplay();

	private InternalObjectDisplay timeDisplay = new InternalObjectDisplay();

	private InternalObjectDisplay fileDisplay = new InternalObjectDisplay();

	private JDesktopPane getDesktopPane() {
		JDesktopPane desk = new JDesktopPane();
		desk.add(requestDisplay);
		desk.add(responseDisplay);
		desk.add(timeDisplay);
		desk.add(fileDisplay);
		desk.addComponentListener(new ComponentAdapter() {
			public void componentResized(ComponentEvent e) {
				resizeEvent(e);
			}
		});
		try {
			requestDisplay.setMaximum(true);
			responseDisplay.setMaximum(true);
			timeDisplay.setMaximum(true);
			fileDisplay.setMaximum(true);
		} catch (PropertyVetoException e) {
		}
		return desk;
	}

	public static void main(String[] args) {
		try {
			UIManager
					.setLookAndFeel("com.incors.plaf.kunststoff.KunststoffLookAndFeel");
		} catch (Throwable e) {
			// e.printStackTrace();
		}
		System.out.println(VERSION);
		// read logfile
		InputStream in = Main.class.getResourceAsStream("/logging.properties");
		if (in != null) {
			try {
				LogManager.getLogManager().readConfiguration(in);
			} catch (Exception e1) {
				e1.printStackTrace();
			} finally {
				if (in != null) {
					try {
						in.close();
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		}
		new Main();
	}

	/** Resize handling */
	private Dimension oldSize = null;

	private void resizeEvent(ComponentEvent e) {
		if (e.getSource() instanceof JDesktopPane) {
			JDesktopPane deskPane = (JDesktopPane) e.getSource();

			if (oldSize == null)
				oldSize = deskPane.getSize();
			double deltaX = deskPane.getSize().getWidth() / oldSize.getWidth();
			double deltaY = deskPane.getSize().getHeight()
					/ oldSize.getHeight();
			JInternalFrame[] frames = deskPane.getAllFrames();
			for (int i = 0; i < frames.length; i++) {
				Rectangle rect = frames[i].getNormalBounds();
				Rectangle newRect = new Rectangle((int) (rect.x * deltaX),
						(int) (rect.y * deltaY), (int) (rect.width * deltaX),
						(int) (rect.height * deltaY));
				frames[i].setBounds(newRect);
			}
			oldSize = deskPane.getSize();
		}
	}

	private int openConnections = 0;

	public void applicationStateChanged(ApplicationEvent ev) {
		switch (ev.getStatus()) {
		case ApplicationEvent.SERVER_STARTED:
			startStopButton.setSelected(true);
			break;
		case ApplicationEvent.SERVER_STOPPED:
			startStopButton.setSelected(false);
			break;
		case ApplicationEvent.CONNECTION_ESTABLISHED:
			connectionCount.setText("Connections: " + (++openConnections));
			break;
		case ApplicationEvent.CONNECTION_CLOSED:
			openConnections = Math.max(0, --openConnections);
			connectionCount.setText("Connections: " + openConnections);
			break;
		default:
			break;
		}
	}

	/**
	 * Returns false if the server is set to proxy mode, true if the server is
	 * set to http-mode.
	 *
	 * @deprecated use getMode instead
	 */
	private boolean isHttpMode() {

		// if ("http".equals(Configuration.getPrefs().get(SERVER_SERVER, "http"))) {

		if (ConfigConstants.MODE_HTTP.equals(getMode())) {
			return true;
		} else {
			return false;
		}
	}

	private String mode = ConfigConstants.MODE_HTTP;

	/**
	 * Returns the mode the server is running in
	 *
	 * @return
	 */
	private String getMode() {

		// return Configuration.getPrefs().get(SERVER_SERVER,MODE_HTTP);
		return mode;
	}

	private class ConfigurationDialog extends JDialog {

		public ConfigurationDialog(Frame owner) throws BackingStoreException {
			super(owner);
			setTitle(VERSION);
			ConfigDialog dl = new ConfigDialog(this, getMode());
			dl.addWindowListener(new WindowAdapter() {
				public void windowClosing(WindowEvent e) {
					close();
				}
			});
			getContentPane().add(dl);
			pack();
			show();
		}

		private void close() {
			this.dispose();
			if (server != null && server.isAlive()) {

				server.stop();
				server.start();
			}
		}
	}
}
